use super::files::write_to_file;
use anyhow::{anyhow, Context, Result};
use std::fs;
use std::fs::File;
use std::io::{copy, Cursor, Read, Seek, Write};
use std::path::{Path, PathBuf};
use walkdir::{DirEntry, WalkDir};
use zip::write::FileOptions;

///解压
/// test.zip文件解压到d:/test文件夹下
///     let zipfile = std::fs::File::open(&zip_file_path).context(format!("打开文件{:?}失败", zip_file_path))?;
///
pub fn extract_v8_to_fs(zip_file: &Vec<u8>, target_input: &str) -> Result<()> {
    let mut file = Cursor::new(zip_file);
    let mut zip = zip::ZipArchive::new(file).context(format!("打开文件失败"))?;

    let mut target = String::from(target_input);

    for i in 0..zip.len() {
        let mut file = zip.by_index(i)?;

        tracing::debug!("Filename: {} {:?}", file.name(), file.sanitized_name());

        if file.is_dir() {
            tracing::debug!("file utf8 path {:?}", file.name_raw());
            //文件名编码,在windows下用winrar压缩的文件夹，中文文夹件会码(发现文件名是用操作系统本地编码编码的，我的电脑就是GBK),本例子中的压缩的文件再解压不会出现乱码
            // let mut file_name = String::from(target);
            // file_name.push_str(&file.name().replace("\\", ""));
            //创建目录
        } else {
            let mut data = Vec::new();
            //
            file.read_to_end(&mut data).map_err(|e| {
                tracing::error!("{}", e);
            });
            tracing::debug!("write_to_file {:?}|{:?}", target, file.name());
            write_file(&target, &file.name(), &data)?;
        }
    }
    Ok(())
}

pub fn write_file(path: &String, filename: &str, content: &Vec<u8>) -> Result<()> {
    //
    let mut file_path = PathBuf::from(path.as_str()).join(filename);
    let dir_path = file_path.parent().ok_or(anyhow!("获取父路径失败"))?;
    std::fs::create_dir_all(dir_path)?;
    //创建文件
    let mut output = File::create(file_path)?;
    output.write_all(content)?;
    Ok(())
}

/// 压缩文件夹
/// test文件夹下有a.jpg和b.txt 两个文件
/// 压缩成test.zip文件
pub fn compress_dir(src_dir_str: &str, target_str: &str) -> Result<()> {
    let zipfile = std::fs::File::create(target_str).map_err(|err| anyhow::anyhow!("{}", err))?;
    let dir = WalkDir::new(src_dir_str);
    let src_dir = PathBuf::from(src_dir_str);
    //
    zip_dir(
        &mut dir.into_iter().filter_map(|e| e.ok()),
        src_dir.to_str().unwrap(),
        zipfile,
    )
    .map_err(|err| anyhow::anyhow!("{}", err))?;
    Ok(())
}

/// 压缩文件
pub fn compress_file(src_dir_str: &str, target_str: &str) -> Result<()> {
    let zipfile = std::fs::File::create(target_str).map_err(|err| anyhow::anyhow!("{}", err))?;
    let dir = WalkDir::new(src_dir_str);

    let src_dir = PathBuf::from(src_dir_str);
    // src_dir.parent().map( |p| println!("{:?}",p.to_str().unwrap()));
    let prefix = src_dir
        .parent()
        .map_or_else(|| "/", |p| p.to_str().unwrap());

    zip_dir(&mut dir.into_iter().filter_map(|e| e.ok()), prefix, zipfile)
        .map_err(|err| anyhow::anyhow!("{}", err))?;
    Ok(())
}

fn zip_dir<T>(
    it: &mut dyn Iterator<Item = DirEntry>,
    prefix: &str,
    writer: T,
) -> zip::result::ZipResult<()>
where
    T: Write + Seek,
{
    let mut zip = zip::ZipWriter::new(writer);
    let options = FileOptions::default()
        .compression_method(zip::CompressionMethod::Bzip2) //直接用了bzip2压缩方式，其它参看枚举
        .unix_permissions(0o755); //unix系统权限

    let mut buffer = Vec::new();
    for entry in it {
        let path = entry.path();
        //zip压缩一个文件时，会把它的全路径当成文件名(在下面的解压函数中打印文件名可知)
        //这里是去掉目录前缀
        println!("prefix  {:?} ...", prefix);
        let name = path.strip_prefix(Path::new(prefix)).unwrap();
        println!("name  {:?} ...", name);
        // Write file or directory explicitly
        // Some unzip tools unzip files with directory paths correctly, some do not!
        if path.is_file() {
            println!("adding file {:?} as {:?} ...", path, name);
            zip.start_file_from_path(name, options)?;
            let mut f = File::open(path)?;

            f.read_to_end(&mut buffer)?;
            zip.write_all(&*buffer)?;
            buffer.clear();
        } else if name.as_os_str().len() != 0 {
            //目录
            // Only if not root! Avoids path spec / warning
            // and mapname conversion failed error on unzip
            println!("adding dir {:?} as {:?} ...", path, name);
            zip.add_directory_from_path(name, options)?;
        }
    }
    zip.finish()?;
    Result::Ok(())
}

///解压
/// test.zip文件解压到d:/test文件夹下
///
fn extract(test: &Path, mut target: &Path) {
    let zipfile = std::fs::File::open(&test).unwrap();
    let mut zip = zip::ZipArchive::new(zipfile).unwrap();

    if !target.exists() {
        fs::create_dir_all(target).map_err(|e| {
            println!("{}", e);
        });
    }
    for i in 0..zip.len() {
        let mut file = zip.by_index(i).unwrap();
        println!("Filename: {} {:?}", file.name(), file.sanitized_name());
        if file.is_dir() {
            println!("file utf8 path {:?}", file.name_raw()); //文件名编码,在windows下用winrar压缩的文件夹，中文文夹件会码(发现文件名是用操作系统本地编码编码的，我的电脑就是GBK),本例子中的压缩的文件再解压不会出现乱码
            let target = target.join(Path::new(&file.name().replace("\\", "")));
            fs::create_dir_all(target).unwrap();
        } else {
            let file_path = target.join(Path::new(file.name()));
            let mut target_file = if !file_path.exists() {
                println!("file path {}", file_path.to_str().unwrap());
                fs::File::create(file_path).unwrap()
            } else {
                fs::File::open(file_path).unwrap()
            };
            copy(&mut file, &mut target_file);
            // target_file.write_all(file.read_bytes().into());
        }
    }
}

fn create_dir(path: &Path) -> Result<(), std::io::Error> {
    fs::create_dir_all(path)
}
